
/**
 * @ProjectName: 
 * @Copyright: 2014 lisheng  All Right Reserved.
 * @address: toughheart@163.com
 * @date: 2016年1月12日 下午8:26:35
 * @Description: 本内容未经本人允许禁止使用、转发.
 */
 
package com.ls.fw.auth.oauth2.as.controller;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.oltu.oauth2.as.request.OAuthAuthzRequest;
import org.apache.oltu.oauth2.as.request.OAuthTokenRequest;
import org.apache.oltu.oauth2.as.response.OAuthASResponse;
import org.apache.oltu.oauth2.as.response.OAuthASResponse.OAuthTokenResponseBuilder;
import org.apache.oltu.oauth2.common.OAuth;
import org.apache.oltu.oauth2.common.error.OAuthError;
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.OAuthResponse;
import org.apache.oltu.oauth2.common.message.types.GrantType;
import org.apache.oltu.oauth2.common.message.types.ResponseType;
import org.apache.oltu.oauth2.common.utils.OAuthUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.alibaba.fastjson.JSON;
import com.ls.fw.auth.oauth2.as.def.Constant;
import com.ls.fw.auth.oauth2.as.exception.OAuth2Exception;
import com.ls.fw.auth.oauth2.as.handler.OAuth2Handler;
import com.ls.fw.auth.oauth2.as.service.OAuthService;
import com.ls.fw.data.bean.ResultTemplate;
import com.ls.fw.mvc.springmvc.base.BaseSimpleController;


/**
 * 
 * @author lisheng
 * @date 2016年1月12日 下午8:26:35
 * @version V1.0 
 */
@Controller
@RequestMapping("oauth2")
public class OAuth2Controller extends BaseSimpleController{
	
	
	@Autowired
    private OAuthService oAuthService;
	@Autowired
	private OAuth2Handler oauth2Handler ;
    /**
     * 构建OAuth2授权请求 [需要client_id与redirect_uri绝对地址]
     * @param request
     * @param session
     * @param model
     * @return 返回授权码(code)有效期10分钟，客户端只能使用一次[与client_id和redirect_uri一一对应关系]
     * @throws OAuthSystemException
     * @throws IOException
     * @url  /oauth2/authorize?client_id={AppKey}&response_type=code&redirect_uri=[YourSiteUrl]&scope=&state=
     * @url  /oauth2/authorize?client_id={AppKey}&response_type=token&redirect_uri=[YourSiteUrl]&scope=&state=
     * @url  /oauth2/authorize?client_id={AppKey}&grant_type=password&username={your username}&password={your password}&scope=&state=
     * @url  /oauth2/authorize?client_id={AppKey}&grant_type=client_credentials&scope=&state=
     */
    @RequestMapping(value = "/authorize")
    public Object authorize(
            Model model,HttpSession session,
            HttpServletRequest request)
            throws URISyntaxException, OAuthSystemException {
    	 HttpHeaders headers = new HttpHeaders();
         headers.add("Content-Type", "application/json; charset=utf-8");
         headers.add("Cache-Control", "no-store");
         headers.add("Pragma", "no-cache");
        try {
            //构建OAuth 授权请求
            OAuthAuthzRequest oauthRequest = new OAuthAuthzRequest(request);
            //进行OAuth响应构建
            OAuthASResponse.OAuthAuthorizationResponseBuilder builder =
                    OAuthASResponse.authorizationResponse(request, HttpServletResponse.SC_FOUND);
            String responseType = oauthRequest.getParam(OAuth.OAUTH_RESPONSE_TYPE);
            String grantType = oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE);
            if (OAuth.OAUTH_CODE.toString().equals(responseType)) {
	            //授权码模式（authorization code）是功能最完整、流程最严密的授权模式。
            	//它的特点就是通过客户端的后台服务器，与"服务提供商"的认证服务器进行互动。
            	String code = oauth2Handler.getAuthCode(oauthRequest);
	           //设置授权码
	            builder.setCode(code);
            }else if (ResponseType.TOKEN.toString().equals(responseType)) {
            	//简化模式（implicit grant type）不通过第三方应用程序的服务器，
            	//直接在浏览器中向认证服务器申请令牌，跳过了"授权码"这个步骤，
            	//因此得名。所有步骤在浏览器中完成，令牌对访问者是可见的，且客户端不需要认证。 
            	final String accessToken = this.oauth2Handler.getAccessToken(oauthRequest);
            	 builder.setParam(OAuth.OAUTH_TOKEN_TYPE, OAuth.DEFAULT_TOKEN_TYPE.toString());
                 builder.setAccessToken(accessToken);
            	 builder.setExpiresIn(oAuthService.getExpireIn(oauthRequest.getClientId()));
            }else if (GrantType.PASSWORD.toString().equals(grantType)) {
            	//密码模式（Resource Owner Password Credentials Grant）中，用户向客户端提供自己的用户名和密码。
            	//客户端使用这些信息，向"服务商提供商"索要授权。
            	//在这种模式中，用户必须把自己的密码给客户端，但是客户端不得储存密码。
            	//这通常用在用户对客户端高度信任的情况下，比如客户端是操作系统的一部分，
            	//或者由一个著名公司出品。而认证服务器只有在其他授权模式无法执行的情况下，
            	//才能考虑使用这种模式。 
            	final String accessToken = this.oauth2Handler.getAccessToken(oauthRequest);
	        	 builder.setParam(OAuth.OAUTH_TOKEN_TYPE, OAuth.DEFAULT_TOKEN_TYPE.toString());
	             builder.setAccessToken(accessToken);
	        	 builder.setExpiresIn(oAuthService.getExpireIn(oauthRequest.getClientId()));
            }else if (GrantType.CLIENT_CREDENTIALS.toString().equals(grantType)) {
	           	//客户端模式（Client Credentials Grant）指客户端以自己的名义，
            	//而不是以用户的名义，向"服务提供商"进行认证。严格地说，
            	//客户端模式并不属于OAuth框架所要解决的问题。在这种模式中，
            	//用户直接向客户端注册，客户端以自己的名义要求"服务提供商"提供服务，
            	//其实不存在授权问题。
            	//必须以某种方式，验证客户端身份 
            	final String accessToken = this.oauth2Handler.getAccessToken(oauthRequest);
	        	 builder.setParam(OAuth.OAUTH_TOKEN_TYPE, OAuth.DEFAULT_TOKEN_TYPE.toString());
	             builder.setAccessToken(accessToken);
	        	 builder.setExpiresIn(oAuthService.getExpireIn(oauthRequest.getClientId()));
           }else{
                OAuthResponse response = OAuthASResponse
                        .errorResponse(HttpServletResponse.SC_BAD_REQUEST)
                        .setError(OAuthError.TokenResponse.INVALID_GRANT)
                        .setErrorDescription("invalid "+OAuth.OAUTH_GRANT_TYPE+"/"+OAuth.OAUTH_RESPONSE_TYPE)
                        .buildJSONMessage();
                return new ResponseEntity(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
            }
            builder.setScope(oauthRequest.getParam(OAuth.OAUTH_SCOPE));
            builder.setParam(OAuth.OAUTH_STATE, oauthRequest.getParam(OAuth.OAUTH_STATE));
            //得到到客户端重定向地址
            String redirectURI = oauthRequest.getParam(OAuth.OAUTH_REDIRECT_URI);
            //构建响应
            final OAuthResponse response = builder.location(redirectURI).buildQueryMessage();
            this.logger.info(response.getLocationUri());
            //根据OAuthResponse返回ResponseEntity响应
            URI url = new URI(response.getLocationUri());
            url.toURL().openConnection();
            headers.setLocation(url);
            return new ResponseEntity(headers, HttpStatus.valueOf(response.getResponseStatus()));
        } catch (OAuthProblemException e) {

            //出错处理
            String redirectUri = e.getRedirectUri();
            if (OAuthUtils.isEmpty(redirectUri)) {
                //告诉客户端没有传入redirectUri直接报错
                ResultTemplate status = new ResultTemplate();
                status.setStatus(HttpStatus.NOT_FOUND.value());
                status.setMsg(Constant.INVALID_REDIRECT_URI);
                
                return new ResponseEntity(JSON.toJSONString(status), headers ,HttpStatus.NOT_FOUND);
            }

            //返回错误消息（如?error=）
            final OAuthResponse response =
                    OAuthASResponse.errorResponse(HttpServletResponse.SC_NOT_FOUND)
                            .error(e).location(redirectUri).buildQueryMessage();
            headers.setLocation(new URI(response.getLocationUri()));
            return new ResponseEntity(headers, HttpStatus.valueOf(response.getResponseStatus()));
        } catch (OAuth2Exception e) {
            OAuthResponse response =
                    OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
                            .setError(e.getCode())
                            .setErrorDescription(e.getMessage())
                            .buildJSONMessage();
            return new ResponseEntity(response.getBody(),headers, HttpStatus.valueOf(response.getResponseStatus()));
       
		} catch (MalformedURLException e) {
            ResultTemplate status = new ResultTemplate();
            status.setStatus(HttpStatus.NOT_FOUND.value());
            status.setMsg(Constant.INVALID_REDIRECT_URI);
            return new ResponseEntity(JSON.toJSONString(status), headers ,HttpStatus.NOT_FOUND);
		} catch (IOException e) {
			HttpHeaders responseHeaders = new HttpHeaders();
            ResultTemplate status = new ResultTemplate();
            status.setStatus(HttpStatus.NOT_FOUND.value());
            status.setMsg(Constant.INVALID_REDIRECT_URI);
            return new ResponseEntity(JSON.toJSONString(status), headers ,HttpStatus.NOT_FOUND);
		}
    }
    
    
    /**
     *  grant_type：表示使用的授权模式，必选项，此处的值固定为"authorization_code"。
		code：表示上一步获得的授权码，必选项。
		redirect_uri：表示重定向URI，必选项，且必须与A步骤中的该参数值保持一致。
		client_id：表示客户端ID，必选项。
     * @author lisheng
     * @date 2016年1月14日 下午8:38:40
     * @version V1.0
     * @param request
     * @return
     * @throws URISyntaxException
     * @throws OAuthSystemException
     * @url  /oauth2/token?grant_type=authorization_code&code={code}&scope=&state=
     * @url  /oauth2/token?grant_type=refresh_token&refresh_token={refresh_token}&scope=&state=
    */
    @SuppressWarnings("rawtypes")
    @RequestMapping(value = "/token",method = RequestMethod.POST)
    public HttpEntity token(HttpServletRequest request)
            throws URISyntaxException, OAuthSystemException {
    	 HttpHeaders headers = new HttpHeaders();
         headers.add("Content-Type", "application/json; charset=utf-8");
         headers.add("Cache-Control", "no-store");
         headers.add("Pragma", "no-cache");
        try {
            //构建OAuth请求
            OAuthTokenRequest oauthRequest = new OAuthTokenRequest(request);
            //生成OAuth响应
            OAuthTokenResponseBuilder builder = OAuthASResponse
                    .tokenResponse(HttpServletResponse.SC_OK);
            String grantType = oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE);
            if(GrantType.REFRESH_TOKEN.toString().equals(grantType)){
               builder.setRefreshToken(oauth2Handler.getRefreshToken(oauthRequest));
            }else if(GrantType.AUTHORIZATION_CODE.toString().equals(grantType)){
                builder.setRefreshToken(oauth2Handler.getRefreshToken(oauthRequest));
                final String accessToken = this.oauth2Handler.getAccessTokenByCode(oauthRequest);
                builder.setAccessToken(accessToken)
               .setTokenType(OAuth.DEFAULT_TOKEN_TYPE.toString())
               .setRefreshToken(oauth2Handler.getRefreshToken(oauthRequest))
               .setExpiresIn(String.valueOf(oAuthService.getExpireIn(oauthRequest.getClientId())));
            }else {
                OAuthResponse response = OAuthASResponse
                        .errorResponse(HttpServletResponse.SC_BAD_REQUEST)
                        .setError(OAuthError.TokenResponse.INVALID_GRANT)
                        .setErrorDescription("invalid "+OAuth.OAUTH_GRANT_TYPE)
                        .buildJSONMessage();
                return new ResponseEntity(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
            }
            builder.setScope(oauthRequest.getParam(OAuth.OAUTH_SCOPE));
            //生成OAuth响应
            OAuthResponse response = builder.buildJSONMessage();

            //根据OAuthResponse生成ResponseEntity
            return new ResponseEntity(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));

        } catch (OAuthProblemException e) {
            //构建错误响应
            OAuthResponse res = OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST).error(e)
                    .buildJSONMessage();
            return new ResponseEntity(res.getBody(),headers, HttpStatus.valueOf(res.getResponseStatus()));
        } catch (OAuth2Exception e) {
            OAuthResponse response =
                    OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
                            .setError(e.getCode())
                            .setErrorDescription(e.getMessage())
                            .buildJSONMessage();
            return new ResponseEntity(response.getBody(),headers, HttpStatus.valueOf(response.getResponseStatus()));
		}
    }
}
